/*
 *only support for "exact division"
 *
 *
 * */



#include <stdlib.h>
#include <stdio.h>
#include "slave.h"
#include "2d5p_aceMesh.h"
#include "task.h"


#define alpha_d 0.0876
#define beta_d  0.0765

__thread_local volatile unsigned long get_reply,put_reply;


//core region 
__thread_local_fix double *A_lhs_array = NULL;
__thread_local_fix double *A_rhs_array = NULL;
//up and down shade region
__thread_local_fix double *A_us_array;
__thread_local_fix double *A_ds_array;
//left and right shade  region
__thread_local_fix double *A_ls_array;
__thread_local_fix double *A_rs_array;
//Record the logical information of the last time executed task.
__thread_local int last_iter,last_starti,last_startj;

#define A_rhs(i,j) A_rhs_array[(i)*(s_endj-s_startj)+(j)]
#define A_lhs(i,j) A_lhs_array[(i)*(s_endj-s_startj)+(j)]
#define A_rhs_unreuse(i,j) A_rhs_array[(i)*(s_endj-s_startj+2)+(j)]
#define A_us(i,j) A_us_array[(i)*(s_endj-s_startj)+(j)]
#define A_ds(i,j) A_ds_array[(i)*(s_endj-s_startj)+(j)]
#define A_ls(i,j) A_ls_array[(i)*(s_endj-s_startj)+(j)]
#define A_rs(i,j) A_rs_array[(i)*(s_endj-s_startj)+(j)]

extern double A[2][SIZEX][SIZEY];

void stencil_core(targs* args)
{
   int dest,src,i,j,s_iter,s_starti,s_endi,s_startj,s_endj;
   int lhs_size;
   int rhs_size;
   int bsize,stride,tsize; 
   s_iter = args->iter;
   dest = (s_iter) % 2 ;
   src = 1 - dest;
   s_starti = args->starti;
   s_endi = args->endi;
   s_startj = args->startj;
   s_endj = args->endj;
   lhs_size = (s_endi-s_starti)*(s_endj-s_startj);
   rhs_size = (s_endi-s_starti+2)*(s_endj-s_startj+2);
   if(A_lhs_array == NULL)
      A_lhs_array = (double *)ldm_malloc(lhs_size * sizeof(double));
   if( A_rhs_array == NULL )
      A_rhs_array = (double *)ldm_malloc(rhs_size * sizeof(double));
   if((s_iter-1) == last_iter && s_starti == last_starti && s_startj == last_startj)
   {
     int stride,len,bsize;
     A_us_array = &A_rhs_array[lhs_size];
     A_ds_array = &A_rhs_array[lhs_size+(s_endj-s_startj)];
     A_ls_array = &A_rhs_array[lhs_size+(s_endj-s_startj)*2];
     A_rs_array = &A_rhs_array[lhs_size+(s_endj-s_startj)*2+(s_endi-s_starti)];
     get_reply = 0;
     //1.1 update up and down shade data    
     athread_get(PE_MODE,&A[src][s_starti-1][s_startj],&A_us(0,0),(s_endj-s_startj)*8,&get_reply,0,0,0); 
     athread_get(PE_MODE,&A[src][s_endi][s_startj], &A_ds(0,0),(s_endj-s_startj)*8,&get_reply,0,0,0);
     //1.2 update left and right shade data
     stride = (SIZEY-1)*8;
     len = (s_endi-s_starti)*8;
     bsize = 8;
     athread_get(PE_MODE,&A[src][s_starti][s_startj-1]
                   , &A_ls(0,0)
                   , len, &get_reply
                   , 0, stride, bsize);
     athread_get(PE_MODE,&A[src][s_starti][s_endj]
                   , &A_rs(0,0)
                   , len,&get_reply
                   , 0, stride, bsize);  
     while(get_reply != 4);
     
     //2.compute core data
     for(i = 1; i < s_endi-s_starti-1; i++)
       for(j = 1; j < s_endj-s_startj-1; j++)
          A_rhs(i,j) = alpha_d * (A_lhs(i,j)) + \
                     beta_d * (A_lhs(i-1,j) + A_lhs(i+1,j) +\
                     A_lhs(i,j-1) + A_lhs(i,j+1));
     //3.1compute up_left_point
     A_rhs(0,0) = alpha_d * (A_lhs(0,0)) + \ 
                   beta_d * (A_us(0,0) + A_lhs(1,0) +\
                   A_ls(0,0) + A_lhs(0,1)); 
     //3.2compute up_right_point
     A_rhs(s_endi-s_starti-1,0) =   alpha_d * (A_lhs(s_endi-s_starti-1,0)) +\
                     beta_d * (A_lhs(s_endi-s_starti-2,0) + A_ds(0,0) +\
                     A_ls(0,s_endi-s_starti-1) + A_lhs(s_endi-s_starti-1,1));
     //3.3compute up_right_point
     A_rhs(0,s_endj-s_startj-1) = alpha_d * (A_lhs(0,s_endj-s_startj-1)) + \ 
                     beta_d * (A_us(0,s_endj-s_startj-1) +\
                     A_lhs(1,s_endj-s_startj-1) +\
                     A_lhs(0,s_endi-s_starti-2) + A_rs(0,0));
     //3.4compute down_right_point
     A_rhs(s_endi-s_starti-1,s_endj-s_startj-1) = alpha_d * \
                     (A_lhs(s_endi-s_starti-1,s_endj-s_startj-1)) + \
                     beta_d * (A_lhs(s_endi-s_starti-2,s_endj-s_startj-1) + \
                     A_ds(0,s_endj-s_startj-1) + \
                     A_lhs(s_endi-s_starti-1,s_endj-s_startj-2) + \
                     A_rs(0,s_endi-s_starti-1));
     //4.1 compute up and down line of j dimensional  change 
     for(j = 1;j < s_endj-s_startj-1; ++j)
     {
       A_rhs(0,j) = alpha_d * (A_lhs(0,j)) + \
                     beta_d * (A_us(0,j) + A_lhs(1,j) +\
                     A_lhs(0,j-1) + A_lhs(0,j+1));
       A_rhs(s_endi-s_starti-1,j) = alpha_d * (A_lhs(s_endi-s_starti-1,j)) + \
                     beta_d * (A_lhs(s_endi-s_starti-2,j) + A_ds(0,j) +\
                     A_lhs(s_endi-s_starti-1,j-1) + A_lhs(s_endi-s_starti-1,j+1));
     }
     //4.2 compute left and right line of i dimensional change
     for(i = 1;i < s_endi-s_starti-1; ++i)
     {
        A_rhs(i,0) = alpha_d * (A_lhs(i,0)) + \
                     beta_d * (A_lhs(i-1,0) + A_lhs(i+1,0) +\
                     A_ls(0,i) + A_lhs(i,1));
        A_rhs(i,s_endj-s_startj-1) = alpha_d * (A_lhs(i,s_endj-s_startj-1)) + \
                     beta_d * (A_lhs(i-1,s_endj-s_startj-1) + A_lhs(i+1,s_endj-s_startj-1) +\
                     A_lhs(i,s_endj-s_startj-2) + A_rs(0,i));
     }
     //write back
     put_reply=0;
     bsize=(s_endj-s_startj)*8;
     stride=(SIZEY-(s_endj-s_startj))*8;  //stride: from blocktail to blockhead
     tsize= bsize*(s_endi-s_starti);
     athread_put(PE_MODE,&A_rhs(0,0)
                , &A[dest][s_starti][s_startj]
                ,tsize,&put_reply,stride,bsize);
     while(put_reply!=1); 
   }
   else{
     // 1.fetch data
     get_reply=0;  
     bsize=(s_endj-s_startj+2)*8;
     stride=(SIZEY-(s_endj-s_startj+2))*8;  //stride: from blocktail to blockhead 
     tsize= bsize*(s_endi-s_starti+2);
     athread_get(PE_MODE,&A[src][s_starti-1][s_startj-1]
                 , &A_rhs_unreuse(0,0)
                 , tsize, &get_reply
                 , 0, stride, bsize);
     //2.synchronize, wait for the transfer finish
     while(get_reply!=1);
     //3.compute
     for(i = 1; i < s_endi-s_starti+1; i++)
       for(j = 1; j < s_endj-s_startj+1; j++)
          A_lhs(i-1,j-1) = alpha_d * (A_rhs_unreuse(i,j)) + \
                     beta_d * (A_rhs_unreuse(i-1,j) + A_rhs_unreuse(i+1,j) +\
                     A_rhs_unreuse(i,j-1) + A_rhs_unreuse(i,j+1));
     //4. write back
     put_reply=0;
     bsize=(s_endj-s_startj)*8;
     stride=(SIZEY-(s_endj-s_startj))*8;  //stride: from blocktail to blockhead
     tsize= bsize*(s_endi-s_starti);
     athread_put(PE_MODE,&A_lhs(0,0)
                , &A[dest][s_starti][s_startj]
                ,tsize,&put_reply,stride,bsize);
     //5.synchronize, wait for writing back finish.
     while(put_reply!=1);
  }
   last_iter = s_iter;
   last_starti = s_starti;
   last_startj = s_startj;
  // ldm_free(A_rhs_array,rhs_size * sizeof(double));
  // ldm_free(A_lhs_array,lhs_size * sizeof(double));
}
